vim9script noclear

g:fzf_winnr = -1
g:fzf_tempfile = tempname()

var bufnr: number
var script = $"{expand('<script>:p:h:h')}/bin/preview.sh"
var fzf = "fzf --layout=reverse --border --ansi"
var col = &columns > 130 ? 30 : 25

var action = {
	'ctrl-t': 'tab drop',
	'ctrl-x': 'split',
	'ctrl-v': 'vsplit'
}

export def Run(term: dict<any>, Func: func)
	var cmd: string
	var width: number = float2nr((&columns - col) * 0.9)
	var height: number = float2nr(&lines * 0.6)

	if term->has_key('w')
		width = term.w
	endif

	if term->has_key('h')
		height = term.h
	endif

	cmd = $"{term.cmd} | {fzf} {term.arg} > {g:fzf_tempfile}"

	if !empty(win_gettype())
		return
	endif

	bufnr = term_start([&shell, &shellcmdflag, cmd], {
		cwd: term.cwd,
		hidden: true,
		term_finish: 'close',
		exit_cb: Func,
	})

	g:fzf_winnr = popup_create(bufnr, {
		minwidth: width,
		maxwidth: width,
		minheight: height,
		maxheight: height,
	})
enddef

def RgHandler(job: job, status: number)
	var edit: string = "edit"
	var strlist = readfile(g:fzf_tempfile)
	var args: list<string>
	var len = strlist->len()

	popup_close(g:fzf_winnr)

	if len == 0
		return
	elseif len == 1 && !empty(get(action, strlist[0], ''))
		return
	else
		edit = get(action, strlist[0], "edit")
		strlist->remove(0)
	endif

	args = split(strlist[0], ':')
	if args->empty()
		return
	endif

	exe $"{edit} {args[0]}"
	cursor(str2nr(args[1]), str2nr(args[2]))
enddef

export def Rg(...strlist: list<string>)
	var term: dict<any>
	var fargs: string = fnamemodify(strlist[-1], ':p')
	var prog: string = "rg --vimgrep --color=always --smart-case --"
	var arg: string = $"--preview='sh {script} \{\}' --expect=ctrl-v,ctrl-x,ctrl-t"

	if len(strlist) > 1 && (isdirectory(fargs) || filereadable(fargs))
		fargs =  $"{shellescape(join(strlist[: -2], ' '))} {strlist[-1]}"
	else
		fargs =  shellescape(join(strlist, ' '))
	endif

	term = {
		'cmd': $"{prog} {fargs}",
		'arg': arg,
		'cwd': getcwd(),
	}
	Run(term, function(RgHandler, []))
enddef

def GTagsHandler(job: job, status: number)
	var edit: string = "edit"
	var strlist = readfile(g:fzf_tempfile)
	var args: list<string>
	var len = strlist->len()

	popup_close(g:fzf_winnr)

	if len == 0
		return
	elseif len == 1 && !empty(get(action, strlist[0], ''))
		return
	else
		edit = get(action, strlist[0], "edit")
		strlist->remove(0)
	endif

	args = split(strlist[0], '\s\+')
	exe $"{edit} {args[2]}"
	cursor(str2nr(args[1]), 1)
enddef

export def GTags()
	var term: dict<any>
	var arg: string = $"--with-nth=1 --preview='sh {script} \{2\} \{3\}' --expect=ctrl-v,ctrl-x,ctrl-t"

	term = {
		'cmd': "global -x . | column -t",
		'arg': arg,
		'cwd': getcwd(),
	}
	Run(term, function(GTagsHandler, []))
enddef

def BTagsHandler(job: job, status: number)
	var strlist = readfile(g:fzf_tempfile)
	var str: string

	popup_close(g:fzf_winnr)

	if strlist->empty()
		return
	endif

	str =  matchstr(strlist[0], '\<\d\+\>\s\+')
	cursor(str2nr(str), 1)
enddef

def LinesHandler(job: job, status: number)
	var strlist = readfile(g:fzf_tempfile)
	var str: string

	popup_close(g:fzf_winnr)

	if strlist->empty()
		return
	endif

	cursor(str2nr(strlist[0]), 1)
enddef

export def BTags()
	var term: dict<any>
	var arg: string = $"--with-nth=1,2 --preview='sh {script} \{3\} \{4\}'"

	term = {
		'cmd': $"ctags -x {@%} | column -t",
		'arg': arg,
		'cwd': getcwd(),
	}
	Run(term, function(BTagsHandler, []))
enddef

def TabsHandler(job: job, status: number)
	var numlist: list<number>
	var strlist = readfile(g:fzf_tempfile)

	popup_close(g:fzf_winnr)

	if strlist->empty()
		return
	endif

	if empty(strlist[0])
		exe $"tabn {str2nr(strlist[1])}"
	elseif strlist[0] == 'ctrl-q'
		for s in strlist[1 : ]
			numlist->add(str2nr(s))
		endfor

		for i in reverse(sort(numlist, 'n'))
			if tabpagenr('$') != 1
				exe $":{i}tabclose"
			endif
		endfor
	else
		echomsg strlist
	endif
enddef

export def Tabs()
	var [str, bufnamestr, term] = ['', '', {}]
	var arg = $"-m --expect=ctrl-q"
	var bufnamelist: list<string>

	for tab in range(1, tabpagenr('$'))
		str = bufname(tabpagebuflist(tab)[0])
		bufnamelist->add($"{tab}\t{fnamemodify(str, ':.')}")
	endfor

	bufnamestr = join(bufnamelist, '\n')

	term = {
		'cmd': $"echo -en {shellescape(bufnamestr)}",
		'arg': arg,
		'cwd': getcwd(),
		'w': float2nr((&columns - col) * 0.5),
		'h': float2nr(&lines * 0.5),
	}
	Run(term, function(TabsHandler, []))
enddef

def BuffersHandler(job: job, status: number)
	var numlist: list<number>
	var strlist = readfile(g:fzf_tempfile)

	popup_close(g:fzf_winnr)

	if strlist->empty()
		return
	endif

	var nr = str2nr(strlist[0])

	for tab in range(1, tabpagenr('$'))
		if nr == tabpagebuflist(tab)[0]
			exe $"tabn {tab}"
			return
		endif
	endfor

	exe $"buffer {nr}"
enddef

export def Buffers()
	var [str, bufnamestr, term] = ['', '', {}]
	var bufnamelist: list<string>
	for buf in filter(range(1, bufnr('$')), 'buflisted(v:val) && getbufvar(v:val, "&filetype") != "qf"')
		str = bufname(buf)
		bufnamelist->add($"{buf}\t{fnamemodify(str, ':.')}")
	endfor

	bufnamestr = join(bufnamelist, '\n')

	term = {
		'cmd': $"echo -en {shellescape(bufnamestr)}",
		'arg': "",
		'cwd': getcwd(),
		'w': float2nr((&columns - col) * 0.5),
		'h': float2nr(&lines * 0.5),
	}
	Run(term, function(BuffersHandler, []))
enddef

export def Lines()
	var term: dict<any>

	if empty(@%)
		return
	endif

	term = {
		'cmd': $"cat -n {@%}",
		'arg': "",
		'cwd': getcwd(),
	}
	Run(term, function(LinesHandler, []))
enddef
